﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PersistentManager.Query.Sql;
using PersistentManager.Query.Keywords;
using PersistentManager.Descriptors;
using PersistentManager.Runtime;

namespace PersistentManager.Query.QueryEngine.Database
{
    internal class ExecuteUpdateAction : ActionBase , IQueryBuilderStrategy
    {
        internal ExecuteUpdateAction( ) : base( QueryPart.NONE ) { }

        internal override bool CanExecute
        {
            get
            {
                return true;
            }
        }

        public void Execute( )
        {
            Tokens.AddFormatted( QueryPart.UPDATE , "UPDATE {0} SET " , NamingStrategy.DecorateName( QueryContext.MetaStructure.SchemaName ) );

            int count = 0;

            List<Criteria> updatePartParameters = Syntax.GetQueryByPart( QueryPart.ENTITY_PARAMETERS );

            bool columnRestriction = updatePartParameters.Count( v => !v.Value.IsNull( ) ) > 0;

            RuntimeTransactionScope scope = RuntimeTransactionScope.Current;
            {
                DirtyTrail audit = QueryContext.Audit;

                if ( audit.IsNull( ) )
                    return;

                foreach( DirtyState change in audit )
                {
                    PropertyMetadata property = QueryContext.MetaStructure.PropertyMapping( change.PropertyName );

                    if( property.IsNotNull() && property.IsAutoGenerated )
                        continue;

                    if ( columnRestriction )
                    {
                        if ( !Tokens.ContainsInValue( updatePartParameters , property ) )
                            continue;
                    }

                    Tokens.HasUpdatableStatement = true;
                    Tokens.AddFormatted( QueryPart.UPDATE , "{0}{1}" , ( count > 0 ) ? "," : string.Empty ,
                        SQLBuilderHelper.SetQueryField( change.ColumnName ) );

                    string parameterName = SQLBuilderHelper.CreateNamedParameter( QueryContext.EntityType , QueryContext.QueryType , change.ColumnName );
                    object parameterValue = change.Value ?? DBNull.Value;

                    Tokens.CreateProviderParameter( CurrentProvider , parameterName , parameterValue );
                    Tokens.AddFormatted( QueryPart.UPDATE , " = {0}" , parameterName );
                    count++;                    
                }                
            }

            if ( Tokens.HasUpdatableStatement )
            {
                object[] uniqueKeysValues = MetaDataManager.GetUniqueKeys( QueryContext.EntityInstance );
                string[] uniquekeyNames = MetaDataManager.GetUniqueKeyNames( QueryContext.EntityType );
                bool hasWhereClause = false;

                Tokens.AddFormatted( QueryPart.UPDATE , " WHERE {0} " ,
                    SQLBuilderHelper.PrepareConditionalStatement( CurrentProvider , QueryContext.EntityType , QueryContext.QueryType , ref hasWhereClause , string.Empty ,
                    uniquekeyNames , uniqueKeysValues , Tokens.Parameters ).ToString( ) );
            }
        }
    }
}
